package net.lab0.nebula.gui;

import java.awt.BorderLayout;
import java.awt.Dialog.ModalExclusionType;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

import javax.imageio.ImageIO;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

import net.lab0.nebula.color.AquaColorModel;
import net.lab0.nebula.color.ColorationModel;
import net.lab0.nebula.core.NebulabrotRenderer;
import net.lab0.nebula.core.QuadTreeManager;
import net.lab0.nebula.data.RawMandelbrotData;
import net.lab0.nebula.gui.enums.PowerOfTen;
import net.lab0.nebula.gui.enums.RenderingMethod;
import net.lab0.nebula.gui.enums.Resolution;
import net.lab0.nebula.listener.MandelbrotRendererListener;
import net.lab0.tools.geom.Point;
import net.lab0.tools.geom.Rectangle;

import com.esr.carto.map2.ViewPort;
import com.esr.carto.map2.iface.ViewPortInterface;

@SuppressWarnings("serial")
public class ExportWindow
extends JFrame
{
    private JTextField                 txtPath;
    private File                       fileToSave;
    private JComboBox<Resolution>      cmbxResolution;
    private JComboBox<RenderingMethod> cmbxRenderingMethod;
    private JComboBox<PowerOfTen>      cmbxPoints;
    private JProgressBar               progressBarRendering;
    private JComboBox<String>          cmbxFormat;
    
    private MainWindow                 mainWindow;
    
    public ExportWindow(MainWindow mainWindow)
    {
        this.mainWindow = mainWindow;
        
        setType(Type.POPUP);
        setTitle("Export options");
        setModalExclusionType(ModalExclusionType.APPLICATION_EXCLUDE);
        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        setAlwaysOnTop(true);
        getContentPane().setLayout(new BorderLayout(0, 0));
        
        JPanel panel = new JPanel();
        getContentPane().add(panel, BorderLayout.NORTH);
        GridBagLayout gbl_panel = new GridBagLayout();
        gbl_panel.columnWidths = new int[] { 0, 0, 0, 0 };
        gbl_panel.rowHeights = new int[] { 0, 0, 0, 0, 0, 0, 0, 0 };
        gbl_panel.columnWeights = new double[] { 0.0, 1.0, 0.0, Double.MIN_VALUE };
        gbl_panel.rowWeights = new double[] { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE };
        panel.setLayout(gbl_panel);
        
        JLabel lblOutputFile = new JLabel("Output file");
        GridBagConstraints gbc_lblOutputFile = new GridBagConstraints();
        gbc_lblOutputFile.insets = new Insets(0, 0, 5, 5);
        gbc_lblOutputFile.anchor = GridBagConstraints.EAST;
        gbc_lblOutputFile.gridx = 0;
        gbc_lblOutputFile.gridy = 0;
        panel.add(lblOutputFile, gbc_lblOutputFile);
        
        txtPath = new JTextField();
        txtPath.setEditable(false);
        GridBagConstraints gbc_txtPath = new GridBagConstraints();
        gbc_txtPath.insets = new Insets(0, 0, 5, 5);
        gbc_txtPath.fill = GridBagConstraints.HORIZONTAL;
        gbc_txtPath.gridx = 1;
        gbc_txtPath.gridy = 0;
        panel.add(txtPath, gbc_txtPath);
        txtPath.setColumns(10);
        
        JButton btnBrowse = new JButton("Browse...");
        btnBrowse.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                choosePathToSaveFile();
            }
        });
        GridBagConstraints gbc_btnBrowse = new GridBagConstraints();
        gbc_btnBrowse.insets = new Insets(0, 0, 5, 0);
        gbc_btnBrowse.gridx = 2;
        gbc_btnBrowse.gridy = 0;
        panel.add(btnBrowse, gbc_btnBrowse);
        
        JLabel lblFileFormat = new JLabel("File format");
        GridBagConstraints gbc_lblFileFormat = new GridBagConstraints();
        gbc_lblFileFormat.anchor = GridBagConstraints.EAST;
        gbc_lblFileFormat.insets = new Insets(0, 0, 5, 5);
        gbc_lblFileFormat.gridx = 0;
        gbc_lblFileFormat.gridy = 1;
        panel.add(lblFileFormat, gbc_lblFileFormat);
        
        cmbxFormat = new JComboBox<>();
        Set<String> tmpFormats = new HashSet<>();
        for (String s : ImageIO.getWriterFormatNames())
        {
            tmpFormats.add(s.toUpperCase());
        }
        String[] supportedFormats = tmpFormats.toArray(new String[0]);
        cmbxFormat.setModel(new DefaultComboBoxModel<String>(supportedFormats));
        GridBagConstraints gbc_cmbxFormat = new GridBagConstraints();
        gbc_cmbxFormat.insets = new Insets(0, 0, 5, 5);
        gbc_cmbxFormat.fill = GridBagConstraints.HORIZONTAL;
        gbc_cmbxFormat.gridx = 1;
        gbc_cmbxFormat.gridy = 1;
        panel.add(cmbxFormat, gbc_cmbxFormat);
        
        JLabel lblResolution = new JLabel("Resolution");
        GridBagConstraints gbc_lblResolution = new GridBagConstraints();
        gbc_lblResolution.anchor = GridBagConstraints.EAST;
        gbc_lblResolution.insets = new Insets(0, 0, 5, 5);
        gbc_lblResolution.gridx = 0;
        gbc_lblResolution.gridy = 2;
        panel.add(lblResolution, gbc_lblResolution);
        
        cmbxResolution = new JComboBox<>();
        cmbxResolution.setModel(new DefaultComboBoxModel<Resolution>(Resolution.values()));
        GridBagConstraints gbc_comboBox_1_1 = new GridBagConstraints();
        gbc_comboBox_1_1.insets = new Insets(0, 0, 5, 5);
        gbc_comboBox_1_1.fill = GridBagConstraints.HORIZONTAL;
        gbc_comboBox_1_1.gridx = 1;
        gbc_comboBox_1_1.gridy = 2;
        panel.add(cmbxResolution, gbc_comboBox_1_1);
        
        JLabel lblPoints = new JLabel("Points");
        GridBagConstraints gbc_lblPoints = new GridBagConstraints();
        gbc_lblPoints.anchor = GridBagConstraints.EAST;
        gbc_lblPoints.insets = new Insets(0, 0, 5, 5);
        gbc_lblPoints.gridx = 0;
        gbc_lblPoints.gridy = 3;
        panel.add(lblPoints, gbc_lblPoints);
        
        cmbxPoints = new JComboBox<>();
        cmbxPoints.setModel(new DefaultComboBoxModel<PowerOfTen>(PowerOfTen.values()));
        GridBagConstraints gbc_cmbxPointsForPreview = new GridBagConstraints();
        gbc_cmbxPointsForPreview.insets = new Insets(0, 0, 5, 5);
        gbc_cmbxPointsForPreview.fill = GridBagConstraints.HORIZONTAL;
        gbc_cmbxPointsForPreview.gridx = 1;
        gbc_cmbxPointsForPreview.gridy = 3;
        panel.add(cmbxPoints, gbc_cmbxPointsForPreview);
        
        JButton btnStart = new JButton("Start");
        btnStart.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                doExport();
            }
        });
        
        JLabel lblRenderingMethod = new JLabel("Rendering method");
        GridBagConstraints gbc_lblRenderingMethod = new GridBagConstraints();
        gbc_lblRenderingMethod.insets = new Insets(0, 0, 5, 5);
        gbc_lblRenderingMethod.gridx = 0;
        gbc_lblRenderingMethod.gridy = 4;
        panel.add(lblRenderingMethod, gbc_lblRenderingMethod);
        
        cmbxRenderingMethod = new JComboBox<>();
        cmbxRenderingMethod.setModel(new DefaultComboBoxModel<RenderingMethod>(RenderingMethod.values()));
        GridBagConstraints gbc_cmbxRenderingMethod = new GridBagConstraints();
        gbc_cmbxRenderingMethod.insets = new Insets(0, 0, 5, 5);
        gbc_cmbxRenderingMethod.fill = GridBagConstraints.HORIZONTAL;
        gbc_cmbxRenderingMethod.gridx = 1;
        gbc_cmbxRenderingMethod.gridy = 4;
        panel.add(cmbxRenderingMethod, gbc_cmbxRenderingMethod);
        GridBagConstraints gbc_btnStart = new GridBagConstraints();
        gbc_btnStart.gridwidth = 3;
        gbc_btnStart.gridx = 0;
        gbc_btnStart.gridy = 6;
        panel.add(btnStart, gbc_btnStart);
        
        JPanel panel_1 = new JPanel();
        getContentPane().add(panel_1, BorderLayout.SOUTH);
        GridBagLayout gbl_panel_1 = new GridBagLayout();
        gbl_panel_1.columnWidths = new int[] { 146, 0 };
        gbl_panel_1.rowHeights = new int[] { 14, 0 };
        gbl_panel_1.columnWeights = new double[] { 1.0, Double.MIN_VALUE };
        gbl_panel_1.rowWeights = new double[] { 0.0, Double.MIN_VALUE };
        panel_1.setLayout(gbl_panel_1);
        
        progressBarRendering = new JProgressBar();
        progressBarRendering.setStringPainted(true);
        GridBagConstraints gbc_progressBarRendering = new GridBagConstraints();
        gbc_progressBarRendering.fill = GridBagConstraints.HORIZONTAL;
        gbc_progressBarRendering.anchor = GridBagConstraints.NORTH;
        gbc_progressBarRendering.gridx = 0;
        gbc_progressBarRendering.gridy = 0;
        panel_1.add(progressBarRendering, gbc_progressBarRendering);
        
        // this.pack();
    }
    
    private void choosePathToSaveFile()
    {
        JFileChooser fileChooser = new JFileChooser(".");
        fileChooser.setMultiSelectionEnabled(false);
        fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
        int ret = fileChooser.showSaveDialog(this);
        
        switch (ret)
        {
            case JFileChooser.APPROVE_OPTION:
                fileToSave = fileChooser.getSelectedFile();
                txtPath.setText(fileToSave.getAbsolutePath());
                break;
            
            default:
                break;
        }
    }
    
    private void doExport()
    {
        final QuadTreeManager quadTreeManager = mainWindow.getQuadTreeManager();
        if (quadTreeManager == null)
        {
            mainWindow.showNoQuadTreeError();
            return;
        }
        
        Resolution resolution = cmbxResolution.getItemAt(cmbxResolution.getSelectedIndex());
        

        double aspectRatio = (double)(resolution.xRes) / (double)(resolution.yRes);
        ViewPortInterface viewPort = new ViewPort(new Rectangle(new Point(-2.0, -2.0), new Point(2.0, 2.0)));
        viewPort.setAspectRatio(aspectRatio);
        final NebulabrotRenderer mandelbrotRenderer = new NebulabrotRenderer(resolution.xRes, resolution.yRes, viewPort);
        final ColorationModel colorationModel = new AquaColorModel();
        
        Thread t = null;
        switch (cmbxRenderingMethod.getItemAt(cmbxRenderingMethod.getSelectedIndex()))
        {
            case LINEAR:
                t = new Thread("Mandelbrot renderer thread")
                {
                    public void run()
                    {
                        mandelbrotRenderer.linearRender(cmbxPoints.getItemAt(cmbxPoints.getSelectedIndex()).quantity, 100, 200);
                    };
                };
                break;
            
            case QUADTREE:
                t = new Thread("Mandelbrot renderer thread")
                {
                    public void run()
                    {
                        mandelbrotRenderer.quadTreeRender(cmbxPoints.getItemAt(cmbxPoints.getSelectedIndex()).quantity, 100, 200,
                        quadTreeManager.getQuadTreeRoot());
                    };
                };
                break;
            
            default:
                assert (false);
                break;
        }
        t.start();
        
        mandelbrotRenderer.addMandelbrotRendererListener(new MandelbrotRendererListener()
        {
            @Override
            public void rendererProgress(long current, long total)
            {
                final long originalCurrent = current;
                final long originalTotal = total;
                while (total > Integer.MAX_VALUE)
                {
                    current /= 2;
                    total /= 2;
                }
                
                final int totalInt = (int) total;
                final int currentInt = (int) current;
                
                SwingUtilities.invokeLater(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        progressBarRendering.setMaximum(totalInt);
                        progressBarRendering.setValue(currentInt);
                        progressBarRendering.setString("" + originalCurrent + "/" + originalTotal);
                    }
                });
            }
            
            @Override
            public void rendererFinished(RawMandelbrotData raw)
            {
                final BufferedImage bufferedImage = raw.computeBufferedImage(colorationModel);
                
                SwingUtilities.invokeLater(new Runnable()
                {
                    
                    @Override
                    public void run()
                    {
                        try
                        {
                            ImageIO.write(bufferedImage, cmbxFormat.getItemAt(cmbxFormat.getSelectedIndex()), fileToSave);
                        }
                        catch (IOException e)
                        {
                            JOptionPane.showMessageDialog(ExportWindow.this, e.getMessage(), "Error while saving the file", JOptionPane.ERROR_MESSAGE);
                        }
                        progressBarRendering.setString("Finished");
                    }
                });
            }
            
            @Override
            public void rendererStopped(RawMandelbrotData raw)
            {
                final BufferedImage bufferedImage = raw.computeBufferedImage(colorationModel);
                
                int ret = JOptionPane.showConfirmDialog(ExportWindow.this, "The rendering was stopped before it finished. Would you like to save it anyway ?",
                "Rendering stopped", JOptionPane.YES_NO_OPTION);
                
                if (ret == JOptionPane.YES_OPTION)
                {
                    SwingUtilities.invokeLater(new Runnable()
                    {
                        
                        @Override
                        public void run()
                        {
                            try
                            {
                                ImageIO.write(bufferedImage, cmbxFormat.getItemAt(cmbxFormat.getSelectedIndex()), fileToSave);
                            }
                            catch (IOException e)
                            {
                                JOptionPane.showMessageDialog(ExportWindow.this, e.getMessage(), "Error while saving the file", JOptionPane.ERROR_MESSAGE);
                            }
                            progressBarRendering.setString("Finished");
                        }
                    });
                }
            }
        });
    }
}
